/*
 * @Author: xiaosihan 
 * @Date: 2021-06-12 15:04:44 
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2021-06-13 14:43:02
 */
import _ from "lodash";
import React from "react";
import * as THREE from "three";
import { BufferGeometry, Camera, Scene, ShaderMaterial, WebGLRenderer } from "three";
import { Group3D, Mesh, MeshMaterialType, THREEComponent, Transition } from "xsh-react-three";
import { Vector3 } from "xsh-react-three/lib/Object3D/type";
import { THREEMouseEvent } from "xsh-react-three/lib/Renderer/THREEMouse";


export type Pie3DData = {
    value: number,
    [key: string]: any
}

interface Props {
    datas: Pie3DData[], // 数据
    colors: string[], // 颜色
    height: number, // 高度
    ridus: number, // 半径
    material?: MeshMaterialType | ShaderMaterial;

    title?: (data: any) => React.Component | String;

    position?: Vector3;
    rotation?: Vector3;
    scale?: Vector3;
    // 鼠标相关
    cursor?: string;//-----------------------------鼠标样式
    onMousemove?: (e: THREEMouseEvent) => void;//--鼠标滑动事件
    onMouseleave?: (e: THREEMouseEvent) => void;//-鼠标离开事件
    onMouseenter?: (e: THREEMouseEvent) => void;//-鼠标进入事件
    onMousedown?: (e: THREEMouseEvent) => void;//--鼠标按下事件
    onMouseup?: (e: THREEMouseEvent) => void;//----鼠标弹起事件
    onClick?: (e: THREEMouseEvent) => void;//------鼠标点击事件
}

interface State {
    hoverIndex?: number;
}


export default class Pie3D extends THREEComponent<Props, State> {
    constructor(props: Props) {
        super(props);

        let { datas = [{ value: 1 }] } = this.props;
        // 设置每个扇形的角度
        datas.map((val, index) => {
            this.Tangle.set({
                [`angle_${index}`]: 0,
                [`startAngle_${index}`]: 0
            });
        });
    }

    state: State = {
        hoverIndex: undefined
    }

    static defaultProps = {
        datas: [1],
        colors: ['#f00', '#0f0', '#00f'],
        height: 1,
        ridus: 1
    }

    static pieGeo = new THREE.CylinderBufferGeometry(1, 1, 1, 50, 1, false, 0, Math.PI * 1.9);

    static planeGeo = new THREE.PlaneBufferGeometry(2, 1, 2, 1);

    // 饼模型数组
    pieGeos: BufferGeometry[] = [];

    //面模型数组
    planeGeos: BufferGeometry[] = [];

    Tangle = new Transition();

    // 设置每个扇形的角度参数
    setTanstionAngle() {
        let { datas = [{ value: 1 }] } = this.props;
        let tatolValue = 0; //总值
        let accumulate = 0;  // 累积计数器

        datas.map(d => tatolValue += d.value);

        let i = 0;

        while (this.Tangle.hasKey(`angle_${i}`) || (datas.length > i)) {

            if (datas.length > i && this.Tangle.hasKey(`angle_${i}`)) {
                this.Tangle.set({
                    [`angle_${i}`]: (datas[i].value / tatolValue) * (Math.PI * 2),
                    [`startAngle_${i}`]: (accumulate / tatolValue) * (Math.PI * 2)
                });
            } else if (datas.length > i && !this.Tangle.hasKey(`angle_${i}`)) {
                this.Tangle
                    .set({
                        [`angle_${i}`]: 0,
                        [`startAngle_${i}`]: Math.PI * 2
                    })
                    .set({
                        [`angle_${i}`]: (datas[i].value / tatolValue) * (Math.PI * 2),
                        [`startAngle_${i}`]: (accumulate / tatolValue) * (Math.PI * 2)
                    });
            } else if (datas.length <= i && this.Tangle.hasKey(`angle_${i}`)) {
                this.Tangle
                    .set({
                        [`angle_${i}`]: 0,
                        [`startAngle_${i}`]: Math.PI * 2
                    });
            }

            if (datas.length > i) {
                accumulate += datas[i].value;
            }

            i++;
        }

        while (this.pieGeos.length > datas.length) {
            this.pieGeos.pop()!.dispose();
        }

        while (this.pieGeos.length < datas.length) {
            this.pieGeos.push(Pie3D.pieGeo.clone());
        }

        while (this.planeGeos.length > datas.length) {
            this.planeGeos.pop()!.dispose();
        }

        while (this.planeGeos.length < datas.length) {
            this.planeGeos.push(Pie3D.planeGeo.clone());
        }

    }

    setHoverIndex = _.throttle((hoverIndex) => {
        this.setState({ hoverIndex });
    }, 100);

    // componentDidMount() {
    // }

    // componentDidUpdate() {
    //     this.setTanstionAngle();
    // }

    static tempVector3 = new THREE.Vector3();

    static YVector3 = new THREE.Vector3(0, 1, 0); // 旋转轴

    static ZVector3 = new THREE.Vector3(0, 0, 1); // Z轴

    beforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera) {

        if (!this.Tangle.isDone()) {

            const json = this.Tangle.getJson();

            this.pieGeos.map((pieGeo, index) => {

                let startAngle = json[`startAngle_${index}`] as number;
                let angle = json[`angle_${index}`] as number;

                let position = Pie3D.pieGeo.attributes.position;

                for (let i = 0; i < position.count; i++) {
                    Pie3D.tempVector3.set(
                        position.array[i * 3],
                        0,
                        position.array[(i * 3) + 2]
                    );

                    const { x, z } = Pie3D.tempVector3;

                    //只弧线上面的点才计算
                    if (x !== 0 || z !== 0) {

                        let currentAngle = Pie3D.tempVector3.angleTo(Pie3D.ZVector3);

                        if (x < 0) {
                            currentAngle = ((Math.PI * 2) - currentAngle);
                        }

                        let nowAngle = ((currentAngle / (Math.PI * 1.9)) * angle) + startAngle - currentAngle;

                        Pie3D.tempVector3.applyAxisAngle(Pie3D.YVector3, nowAngle);

                        (() => {
                            const { x, z } = Pie3D.tempVector3;
                            pieGeo.attributes.position.setX(i, x);
                            pieGeo.attributes.position.setZ(i, z);
                        })();

                    }// if

                }// for

                pieGeo.computeBoundingSphere();
                pieGeo.attributes.position.needsUpdate = true;
            });


            this.planeGeos.map((planeGeo, index) => {

                let startAngle = json[`startAngle_${index}`] as number;
                let angle = json[`angle_${index}`] as number;

                let position = Pie3D.planeGeo.attributes.position;

                for (let i = 0; i < position.count; i++) {
                    Pie3D.tempVector3.set(
                        position.array[i * 3],
                        0,
                        position.array[(i * 3) + 2]
                    );

                    const { x, z } = Pie3D.tempVector3;

                    //只弧线上面的点才计算
                    if (x !== 0 || z !== 0) {

                        let currentAngle = Pie3D.tempVector3.angleTo(Pie3D.ZVector3);

                        if (x <= 0) {
                            currentAngle = ((Math.PI * 2) - currentAngle);
                        }

                        let nowAngle = ((currentAngle / Math.PI) * angle) + startAngle - currentAngle;

                        Pie3D.tempVector3.applyAxisAngle(Pie3D.YVector3, nowAngle);

                        (() => {
                            const { x, z } = Pie3D.tempVector3;
                            planeGeo.attributes.position.setX(i, x);
                            planeGeo.attributes.position.setZ(i, z);
                        })();

                    }// if

                }// for

                planeGeo.computeBoundingSphere();
                planeGeo.attributes.position.needsUpdate = true;
            });

        }
    }

    render() {

        this.setTanstionAngle();

        const {
            datas = [{ value: 1 }],
            colors = ['#fff'],
            height = 1,
            ridus = 1,
            material,
            title,

            position,
            rotation,
            scale,

            // 鼠标相关
            cursor,
            onMousemove,
            onMouseleave,
            onMouseenter,
            onMousedown,
            onMouseup,
            onClick,
        } = this.props;

        const { hoverIndex } = this.state;

        return (
            <Group3D
                position={position}
                rotation={rotation}
                scale={scale}
            >
                {
                    this.pieGeos.map((pieGeo, index) => {
                        return (
                            <Mesh
                                key={index}
                                duration={300}
                                geometry={pieGeo}
                                material={material || MeshMaterialType.MeshBasicMaterial}
                                wireframe={false}
                                cursor={cursor}
                                color={colors[index % colors.length]}
                                scale={{
                                    x: ridus * ((hoverIndex === index) ? 1.1 : 1),
                                    y: height,
                                    z: ridus * ((hoverIndex === index) ? 1.1 : 1)
                                }}
                                title={
                                    <span
                                        style={{
                                            position: "relative",
                                            backgroundColor: "rgba(0, 0, 0, 0.8)",
                                            borderRadius: 4,
                                            padding: "4px 8px",
                                            color: "#fff"
                                        }}
                                    >
                                        {title ? title(datas[index]) : `value:${datas[index].value}`}
                                    </span>
                                }

                                onMousemove={onMousemove ? e => onMousemove(Object.assign(e, { data: datas[index], index })) : undefined}

                                onMouseenter={e => {
                                    this.setHoverIndex(index);
                                    onMouseenter && onMouseenter(Object.assign(e, { data: datas[index], index }));
                                }}

                                onMouseleave={e => {
                                    this.setHoverIndex(undefined);
                                    onMouseleave && onMouseleave(Object.assign(e, { data: datas[index], index }));
                                }}

                                onMousedown={onMousedown ? e => onMousedown(Object.assign(e, { data: datas[index], index })) : undefined}
                                onMouseup={onMouseup ? e => onMouseup(Object.assign(e, { data: datas[index], index })) : undefined}
                                onClick={onClick ? e => onClick(Object.assign(e, { data: datas[index], index })) : undefined}
                            />
                        );
                    })
                }

                {
                    this.planeGeos.map((planeGeo, index) => {
                        return (
                            <Mesh
                                key={index}
                                duration={300}
                                geometry={planeGeo}
                                material={material || MeshMaterialType.MeshBasicMaterial}
                                wireframe={false}
                                side={THREE.FrontSide}
                                cursor={cursor}
                                color={colors[index % colors.length]}
                                scale={{
                                    x: ridus * ((hoverIndex === index) ? 1.1 : 1),
                                    y: height,
                                    z: ridus * ((hoverIndex === index) ? 1.1 : 1)
                                }}
                            />
                        );
                    })
                }


            </Group3D >
        );
    }
}

Pie3D.planeGeo.rotateY(Math.PI / -2);